// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the CPL Common Public License version 1.0.
package fitnesse.responders.run;

import java.net.ServerSocket;
import java.net.Socket;

import fitnesse.http.Request;
import fitnesse.responders.PageDataWikiPageResponder;
import fitnesse.responders.editing.PropertiesResponder;
import fitnesse.wiki.PageCrawler;
import fitnesse.wiki.PageData;
import fitnesse.wiki.WikiPage;
import fitnesse.wiki.WikiPageProperties;
import fitnesse.wiki.WikiPageProperty;

public abstract class TestSystem implements TestSystemListener {
	public static final String DEFAULT_COMMAND_PATTERN = "java %c -cp %p %m";
	public static final String COMMAND_CONTEST_PREFERENCES = "-Dcontest.preferences=";
	public static final String COMMAND_JAVAAGENT = " -javaagent:";
	public static final String CONTEST_LIB = "CONTEST_LIB";
	public static final String CONTEST_PROP = "CONTEST_PROP"; 
	protected WikiPage page;
	protected boolean fastTest;
	protected static final String emptyPageContent = "OH NO! This page is empty!";
	protected TestSystemListener testSystemListener;
	protected ExecutionLog log;

	public TestSystem(WikiPage page, TestSystemListener testSystemListener) {
		this.page = page;
		this.testSystemListener = testSystemListener;
	}

	public ExecutionLog getExecutionLog(String classPath,
			TestSystem.Descriptor descriptor) throws Exception {
		log = createExecutionLog(classPath, descriptor);
		return log;
	}

	protected abstract ExecutionLog createExecutionLog(String classPath,
			Descriptor descriptor) throws Exception;

	protected String buildCommand(TestSystem.Descriptor descriptor,
			String classPath) throws Exception {
		String commandPattern = descriptor.commandPattern;
		String command = replace(commandPattern, "%p", classPath);
		command = replace(command, "%m", descriptor.testRunner);

		PageData pageData = page.getData();
		String contestLib = pageData.getVariable(CONTEST_LIB);
		String contestProp = pageData.getVariable(CONTEST_PROP);

		if (fit.FitServer.enableContest) {
			command = replace(command, "%c", COMMAND_CONTEST_PREFERENCES + contestProp + COMMAND_JAVAAGENT + contestLib);
		} else {
			command = replace(command, "%c", "");
		}

		return command;
	}

	private static String getCommandPattern(PageData pageData) throws Exception {
		String testRunner = pageData.getVariable("COMMAND_PATTERN");
		if (testRunner == null) {
			testRunner = DEFAULT_COMMAND_PATTERN;
		} else if (testRunner.startsWith("java")) {
			testRunner = testRunner.substring(0, 4) + " %c " + testRunner.substring(5);
		}
		return testRunner;
	}

	// String.replaceAll(...) is not trustworthy because it seems to remove all
	// '\' characters.
	protected static String replace(String value, String mark,
			String replacement) {
		int index = value.indexOf(mark);
		if (index == -1)
			return value;

		return value.substring(0, index) + replacement
		+ value.substring(index + mark.length());
	}

	public void setFastTest(boolean fastTest) {
		this.fastTest = fastTest;
	}

	public static String getTestSystemName(PageData data) throws Exception {
		String testSystemName = getTestSystem(data);
		String testRunner = getTestRunner(data);
		return String.format("%s:%s", testSystemName, testRunner);
	}

	private static String getTestSystem(PageData data) throws Exception {
		String testSystemName = data.getVariable("TEST_SYSTEM");
		if (testSystemName == null)
			return "fit";
		return testSystemName;
	}

	public static String getPathSeparator(PageData pageData) throws Exception {
		String separator = pageData.getVariable("PATH_SEPARATOR");
		if (separator == null)
			separator = (String) System.getProperties().get("path.separator");
		return separator;
	}

	public static String getTestSystemType(String testSystemName)
	throws Exception {
		String parts[] = testSystemName.split(":");
		return parts[0];
	}

	public void acceptOutputFirst(String output) throws Exception {
		testSystemListener.acceptOutputFirst(output);
	}

	public void acceptResultsLast(TestSummary testSummary) throws Exception {
		testSystemListener.acceptResultsLast(testSummary);
	}

	public void exceptionOccurred(Throwable e) {
		log.addException(e);
		log.addReason("Test execution aborted abnormally with error code "
				+ log.getExitCode());
		testSystemListener.exceptionOccurred(e);
	}

	public abstract void start() throws Exception;

	public static String getTestRunner(PageData data) throws Exception {
		String program = data.getVariable("TEST_RUNNER");
		if (program == null)
			program = defaultTestRunner(data);
		return program;
	}

	static String defaultTestRunner(PageData data) throws Exception {
		String testSystemType = getTestSystemType(getTestSystem(data));
		if ("slim".equalsIgnoreCase(testSystemType))
			return "fitnesse.slim.SlimService";
		else
			return "fit.FitServer";
	}

	public abstract void bye() throws Exception;

	public abstract boolean isSuccessfullyStarted();

	public abstract void kill() throws Exception;

	public abstract String runTestsAndGenerateHtml(PageData pageData)
	throws Exception;

	public static Descriptor getDescriptor(PageData data) throws Exception {
		String testSystemName = getTestSystem(data);
		String testRunner = getTestRunner(data);
		String commandPattern = getCommandPattern(data);
		String pathSeparator = getPathSeparator(data);
		return new Descriptor(testSystemName, testRunner, commandPattern,
				pathSeparator);
	}

	public static class Descriptor {
		public String testSystemName;
		public String testRunner;
		public String commandPattern;
		public String pathSeparator;

		public Descriptor(String testSystemName, String testRunner,
				String commandPattern, String pathSeparator) {
			this.testSystemName = testSystemName;
			this.testRunner = testRunner;
			this.commandPattern = commandPattern;
			this.pathSeparator = pathSeparator;
		}

		public int hashCode() {
			return testSystemName.hashCode() ^ testRunner.hashCode()
			^ commandPattern.hashCode() ^ pathSeparator.hashCode();
		}

		public boolean equals(Object obj) {
			Descriptor d = (Descriptor) obj;
			return d.testSystemName.equals(testSystemName)
			&& d.testRunner.equals(testRunner)
			&& d.commandPattern.equals(commandPattern)
			&& d.pathSeparator.equals(pathSeparator);
		}
	}
}
